import { BaseCanvas } from './base/baseCanvas';
import { DrawOption, IShape, CursorEnum, ControlTypeEnum, Point } from './model';

export class AutoCanvas {

    canvasEle: HTMLCanvasElement;
    ctx: CanvasRenderingContext2D;
    cursor = CursorEnum.Auto;
    selectedShape?: IShape;


    mouseMoveAction?: (e: MouseEvent) => void;
    mouseDownAction?: (e: MouseEvent) => void;
    mouseUpAction?: (e: MouseEvent) => void;

    isMoving = false;

    controlType: boolean | ControlTypeEnum = false;

    mousePosition!: Point;
    mouseDownPosition!: Point;

    private shapeCollections: IShape[] = [];

    constructor(canvasEle: HTMLCanvasElement | string) {
        this.canvasEle = canvasEle instanceof HTMLCanvasElement ? canvasEle :
            document.querySelector(canvasEle) as HTMLCanvasElement;
        this.ctx = this.canvasEle.getContext('2d')!;
    }

    addEventListener() {
        // mouse move
        this.mouseMoveAction = (e: MouseEvent) => {
            this.mousePosition = this.getCanvasMousePoint(e);
            this.checkControls();
        };
        this.canvasEle.addEventListener('mousemove', this.mouseMoveAction);

        // mouse down
        this.mouseDownAction = (e: MouseEvent) => {
            this.isMoving = true;
            this.mouseDownPosition = this.getCanvasMousePoint(e);

            if (!this.controlType) {
                this.renderAll();
                this.checkIsInside();
            }
        };
        this.canvasEle.addEventListener('mousedown', this.mouseDownAction);

        // mouse up
        this.mouseUpAction = (e: MouseEvent) => {
            this.changeCursor(CursorEnum.Auto);
            this.isMoving = false;
        };
        this.canvasEle.addEventListener('mouseup', this.mouseUpAction);
    }

    removeEventListener() {
        if (this.mouseMoveAction) {
            this.canvasEle.removeEventListener('mousemove', this.mouseMoveAction);
        }
        if (this.mouseDownAction) {
            this.canvasEle.removeEventListener('mousedown', this.mouseDownAction);
        }
        if (this.mouseUpAction) {
            this.canvasEle.removeEventListener('mouseup', this.mouseUpAction);
        }
    }

    clearRect() {
        this.ctx.clearRect(0, 0, this.canvasEle.width, this.canvasEle.height);
    }

    render() {
        this.clearRect();
    }

    renderAll() {
        this.selectedShape = undefined;
        this.changeCursor(CursorEnum.Auto);
        this.clearRect();
        this.shapeCollections.forEach(shapeItem => {
            shapeItem.render(this.ctx);
        });
        return this;
    }

    updateAll() {
        this.clearRect();
        this.shapeCollections.forEach(shapeItem => {
            if (this.selectedShape === shapeItem) {
                shapeItem.update(this.mousePosition, this.mouseDownPosition);
            }
            shapeItem.render(this.ctx);
            if (this.selectedShape === shapeItem) {
                shapeItem.drawBorder(this.ctx);
                shapeItem.drawControls(this.ctx);
            }
        });
        return this;
    }

    addShape(...shape: IShape[]) {
        this.shapeCollections.push(...shape);
        this.renderAll();
        return this;
    }

    private checkIsInside() {
        const length = this.shapeCollections.length;
        for (let i = length - 1; i >= 0; i--) {
            const shapeItem = this.shapeCollections[i];
            if (shapeItem.checkClick(this.ctx, this.mousePosition)) {
                this.changeCursor(CursorEnum.Move);
                this.selectedShape = shapeItem;
                break;
            }
        }
    }

    private changeCursor(cursor: CursorEnum) {
        this.canvasEle.style.cursor = cursor;
    }

    checkControls() {
        const selectedShape = this.selectedShape;
        if (!selectedShape) {
            this.changeCursor(CursorEnum.Auto);
            return;
        }
        if (!this.isMoving) {
            const { controlType, cursor } = selectedShape.getOnControlType(this.mousePosition);
            this.controlType = controlType;
            this.changeCursor(cursor);
        } else {
            this.updateAll();
        }
        if (!this.controlType && this.isMoving) {
            this.changeCursor(CursorEnum.Move);
        }
        // const isInside = selectedShape.getIsInControlArea(this.mousePosition);
        // if (!isInside) {
        //     this.changeCursor(CursorEnum.Auto);
        // }
    }

    getCanvasMousePoint(e: MouseEvent) {
        const { left, top, width, height } = this.canvasEle.getBoundingClientRect();
        return {
            x: e.clientX - left * (this.canvasEle.width / width),
            y: e.clientY - top * (this.canvasEle.height / height)
        };
    }
}
